import ctypes
import os
import sys
import time


class EChatIMSDKBridge:
    def __init__(self):
        self.dllPath = ""
        self.jsonConfig = ""
        self.initCb = None
        self.listenerCb = None
        self.handle = None
        self.fileHandleId = 0
        self.initFinished = False
        self.callId = 1
        # 使用callbackRefsMap保持python回调函数的引用, 否则可能被python 回收, 导致从动态库回调python异常
        self.callbackRefsMap = {}
        # api 回调方法
        self.apiHandleCallbackMap = {}

        self.uploadCb = None


    def sdkInitCallback(self, ret, err_msg):
        if ret == 0:
            self.initFinished = True
        else:
            print("sdk init failed, reason = {}".format(err_msg.decode("utf8")))

        if self.initCb:
            self.initCb(ret, err_msg)


    def sdkListenerCallback(self, method, argc, argv):
        if method != "" and argc > 0:
            print("sdkListenerCallback: method = {}, argv[0]={}".format(method, argv[0]))
            if self.listenerCb:
               self.listenerCb(method, argc, argv)




    def sdkApiCallback(self, callId, method, jsonParams):
        methodKey = method.decode('utf-8')
        if self.apiHandleCallbackMap.__contains__(methodKey):
            print("sdkApiCallback: callId = {}, method = {}, argv[0]={}".format(callId, method, jsonParams))
            # 调用回调函数
            cbFunc = self.apiHandleCallbackMap[methodKey]
            cbFunc(jsonParams.decode("utf8"))




    def sdkInit(self, dllPath, jsonConfig, initCb = None, listenerCb = None):
        self.dllPath = dllPath
        self.jsonConfig = jsonConfig
        self.initCb = initCb
        self.listenerCb = listenerCb
        if not os.path.isfile(self.dllPath):
            print("dll file not exist, path:" + self.dllPath)
            return False

        if sys.platform == "win32":
            self.handle = ctypes.WinDLL(self.dllPath)
        else:
            self.handle = ctypes.cdll.LoadLibrary(self.dllPath)

        # 定义函数指针签名 typedef void(*pfunc_init_cb)(int ret, const char *err_msg)
        pfuncInitCbType = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
        # 获取绑定python函数的c函数指针
        pfuncInitCb = pfuncInitCbType(self.sdkInitCallback)
        self.callbackRefsMap["pfuncInitCb"] = pfuncInitCb
        # 定义函数指针签名 typedef void(*pfunc_listener_cb)(const char *method, int argc, const char *argv[])
        pfuncListenerCbType = ctypes.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(ctypes.c_char_p))
        # 获取绑定python函数的c函数指针
        pfuncListenerCb = pfuncListenerCbType(self.sdkListenerCallback)
        self.callbackRefsMap["pfuncListenerCb"] = pfuncListenerCb
        # 定义函数指针签名 typedef void(*pfunc_api_cb)(unsigned long call_id, const char *api_method, const char *response)
        pfuncApiCbType = ctypes.CFUNCTYPE(None, ctypes.c_ulong, ctypes.c_char_p,  ctypes.c_char_p)
        # 获取绑定python函数的c函数指针
        pfuncApiCb = pfuncApiCbType(self.sdkApiCallback)
        self.callbackRefsMap["pfuncApiCb"] = pfuncApiCb
        # 向DLL设置 api 回调函数
        self.handle.sdk_set_api_listener(pfuncApiCb)

        self.handle.sdk_init(jsonConfig.encode('utf-8'), pfuncInitCb, pfuncListenerCb)
        while not self.initFinished:
            time.sleep(1)

        return True


    def apiCall(self, method, params, callbackFunc):
        #  register callbackFunc to apiHandleCallbackMap
        self.apiHandleCallbackMap[str(method)] = callbackFunc
        self.handle.sdk_invoke_api(ctypes.c_ulong(self.callId), method.encode('utf-8'), params.encode('utf-8'))
        self.callId += 1



    def sdkUploadCallback(self, handleId, method, argc, argv):
        methodKey = method.decode('utf-8')
        if methodKey != "" and argc > 1:
            print("sdkListenerCallback: method = {}, argv[0]={}, argv[1]={}".format(methodKey, argv[0], argv[1]))
            if self.uploadCb:
               self.uploadCb(methodKey, argv[0].decode("utf8"), argv[1].decode("utf8"))


    def sdkFileServerInit(self, uploadCb = None):
        if self.handle == None:
            print("sdk not init yet")
            return False

        UPLOAD_CONFIG = "{\"maxFileSize\":100,\"allowSuffix\":[\"jpeg\",\"zip\",\"mp3\"],\"type\":\"FILE\"}"
        self.handle.sdk_fileserver_instance.restype = ctypes.c_long
        fid = self.handle.sdk_fileserver_instance()
        self.fileHandleId = fid
        if self.fileHandleId == 0:
            print("sdk_fileserver_init failed")
            return False
        self.handle.sdk_fileserver_init(ctypes.c_long(self.fileHandleId), UPLOAD_CONFIG.encode('utf-8'))

        # 定义函数指针签名 typedef void(*pfunc_upload_cb)(unsigned long handle_id, const char *method, int argc, const char *argv[])
        pfuncUploadCbType = ctypes.CFUNCTYPE(None, ctypes.c_long, ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(ctypes.c_char_p))
        pfuncUploadCb = pfuncUploadCbType(self.sdkUploadCallback)
        self.callbackRefsMap["pfuncUploadCb"] = pfuncUploadCb
        # 向DLL设置 upload回调函数
        self.handle.sdk_fileserver_set_upload_listener(ctypes.c_long(self.fileHandleId), pfuncUploadCb)

        self.uploadCb = uploadCb
        
        return True

    def sdkFileServerUpload(self, filePath):
        if self.fileHandleId == 0:
           print("sdk_fileserver_init not init yet")
           return False

        self.handle.sdk_fileserver_invoke_upload(ctypes.c_long(self.fileHandleId), filePath.encode('utf-8'), ctypes.c_int(1))
        return True


    def sdkFileServerRelease(self):
        if self.fileHandleId == 0:
           print("sdk_fileserver_init not init yet")
           return False

        self.handle.sdk_fileserver_release(ctypes.c_long(self.fileHandleId))
        return True

#
# typedef void(*pfunc_upload_cb)(unsigned long handle_id, const char *method, int argc, const char *argv[]);
#
# // 以下是fileserver对外暴露的DLL方法
# DLL_EXPORT_SPEC HANDLE_ID_TYPE sdk_fileserver_instance();
# DLL_EXPORT_SPEC void sdk_fileserver_init(HANDLE_ID_TYPE handle_id, const char *json_upload_config);
# DLL_EXPORT_SPEC void sdk_fileserver_set_upload_listener(HANDLE_ID_TYPE handle_id, pfunc_upload_cb upload_cb);
# DLL_EXPORT_SPEC void sdk_fileserver_invoke_upload(HANDLE_ID_TYPE handle_id, const char *filepath, int sub_thread);
# DLL_EXPORT_SPEC void sdk_fileserver_release(HANDLE_ID_TYPE handle_id);



echatimSdk = EChatIMSDKBridge()
